using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using microsoft_TODO.DTOs;
using microsoft_TODO.Models;
using microsoft_TODO.ServiceCore;
using microsoft_TODO.ViewModels;
using server.Helpers;

namespace microsoft_TODO.Controllers
{

    [Route("api/[controller]")]
    [ApiController]
    public class UserController : ControllerBase
    {

        ILogger<UserController> _logger;
        IUserService _userService;
        ITaskService _taskService;
        IMapper _mapper;
        IConfiguration _configuration;

        public UserController(ILogger<UserController> logger,
            IUserService userService,
            ITaskService taskService,
            IMapper mapper,
            IConfiguration configuration)
        {
            _logger = logger;
            _userService = userService;
            _taskService = taskService;
            _mapper = mapper;
            _configuration = configuration;
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="dto"></param>
        /// <remarks>
        /// codes:
        ///
        ///     4003: "用户不存在或密码错误"
        ///
        /// </remarks>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("auth")]
        [ProducesResponseType(200)]
        [ProducesResponseType(404)]
        [ProducesResponseType(400)]
        public ActionResult<AuthResultViewModel> Auth(AuthViewModel model)
        {
            try
            {
                if (model.GrantType == "password")
                {
                    return HandleLogin(model.Username, model.Password);
                }
                else if (model.GrantType == "refresh_token")
                {
                    return HandleRefreshToken(model.Id, model.RefreshToken);
                }
                return BadRequest();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to login");
                return BadRequest();
            }
        }

        /// <summary>
        /// 注册
        /// </summary>
        /// <param name="dto"></param>
        /// <remarks>
        /// codes:
        ///
        ///     2001: "用户名已被注册"
        ///
        /// </remarks>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("register")]
        [ProducesResponseType(201)]
        [ProducesResponseType(400)]
        public ActionResult Register(RegisterDto dto)
        {
            try
            {
                if (_userService.IsUsernameExistent(dto.Username))
                {
                    _logger.LogWarning(LoggingEvents.ItemExistence, "Username({name}) Is Existent", dto.Username);
                    return BadRequest(new { code = LoggingEvents.ItemExistence });
                }
                if (_userService.CreateUser(dto))
                {
                    _logger.LogInformation(LoggingEvents.InsertItem, "User({name}) Inserted", dto.Username);
                    return Created("", new { succ = true });
                }
                return BadRequest();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to register");
                return BadRequest();
            }
        }

        /// <summary>
        /// 根据token获取已登录的用户信息
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [ProducesResponseType(200)]
        [ProducesResponseType(401)]
        [ProducesResponseType(404)]
        public ActionResult<GetUserDto> Get()
        {
            try
            {
                string token = null;
                if (CanGetToken(out token))
                {
                    var handler = new JwtSecurityTokenHandler();
                    if (handler.CanReadToken(token))
                    {
                        var currentUserId = handler.ReadJwtToken(token).Claims.FirstOrDefault(u => u.Type == ClaimTypes.NameIdentifier).Value;
                        var entity = _userService.GetUserById(currentUserId);
                        if (entity.LogonTime.Date != DateTime.Now.Date)
                        {
                            _userService.UpdateLogonTime(entity.Id);
                            _taskService.TurnTodayTasksToFalse(entity.Id);
                        }
                        return _mapper.Map<GetUserDto>(entity);
                    }
                }
                return Unauthorized();
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        private ActionResult<AuthResultViewModel> HandleLogin(string username, string password)
        {
            var entity = _userService.Authenticate(username, password);
            if (entity == null)
            {
                _logger.LogWarning(LoggingEvents.GetItemNotFound, "Login({name}) Failed", username);
                return NotFound(new { code = LoggingEvents.GetItemNotFound });
            }
            else if (entity.LogonTime.Date != DateTime.Now.Date)
            {
                _userService.UpdateLogonTime(entity.Id);
                _taskService.TurnTodayTasksToFalse(entity.Id);
            }
            var result = _mapper.Map<AuthResultViewModel>(entity);
            result.Token = GenerateToken(entity.Id);
            return result;
        }

        private ActionResult<AuthResultViewModel> HandleRefreshToken(string id, string token)
        {
            if (_userService.IsExpiredRefreshToken(id, token))
            {
                return Unauthorized();
            }
            else
            {
                var entity = _userService.GetUserById(id);
                var result = _mapper.Map<AuthResultViewModel>(entity);
                result.Token = GenerateToken(id);
                return result;
            }
        }

        /// <summary>
        /// 生活token并返回
        /// </summary>
        /// <param name="id">user id</param>
        /// <param name="username">user name</param>
        /// <returns></returns>
        private TokenResult GenerateToken(string id)
        {
            var refreshToken = Guid.NewGuid().ToString().Replace("-", "");
            var refreshTokenEntity = new RefreshToken
            {
                UserId = id,
                RefreshDate = DateTime.Now,
                Token = refreshToken
            };
            _userService.UpsertRefreshToken(refreshTokenEntity);

            var claims = new Claim[] {
                new Claim (ClaimTypes.NameIdentifier, id),
            };
            var now = DateTime.UtcNow;
            SymmetricSecurityKey symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.GetSection("Jwt")["Secret"])); // 生成密钥
            SigningCredentials signingCredential = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256); // 生成登录证书
            var jwtToken = new JwtSecurityToken(
                claims: claims,
                notBefore: now,
                expires: now.Add(TimeSpan.FromMinutes(1)),
                //expires: now.Add(TimeSpan.FromDays(1)),
                signingCredentials: signingCredential
            ); // 生成token

            var access_token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
            var response = new TokenResult
            {
                access_token = access_token,
                refresh_token = refreshToken,
                expires_in = (long)TimeSpan.FromMinutes(1).TotalMilliseconds
                //expires_in = (long)TimeSpan.FromDays(1).TotalMilliseconds
            };

            return response;
        }

        /// <summary>
        /// 获取token字符串
        /// </summary>
        /// <param name="token">token</param>
        /// <returns></returns>
        private bool CanGetToken(out string token)
        {
            token = null;
            string authorization = Request.Headers["Authorization"];

            if (string.IsNullOrEmpty(authorization))
            {
                return false;
            }

            if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
            {
                token = authorization.Substring("Bearer ".Length).Trim();
            }

            if (string.IsNullOrEmpty(token))
            {
                return false;
            }
            return true;
        }
    }
}
